ModelSerializer 序列化组件


只要继承了 APIView 就可以使用 ModelSerializer

ModelSerializer 的使用和 ModelForm 组件的使用类似

1.ModelSerializer的基本使用

# serializer.py

from rest_framework import serializers
from .models import *


class BookSerializers(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = '__all__'

# 自定义错误信息
        extra_kwargs = {
            "title": {
                'error_messages': {
                    'required': '书籍名称不能为空'
                }
            },
            'price': {
                'error_messages': {
                    'required': '价格不能为空'
                }
            },
            'pub_date': {
                'error_messages': {
                    'required': '日期不能为空'
                }
            },
            'publish': {
                'error_messages': {
                    'required': '出版社不能为空'
                }
            },
            'author': {
                'error_messages': {
                    'required': '作者不能为空'
                }
            }
        }

# views.py

from django.shortcuts import render, HttpResponse
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import *
from .serializer import *


class BookView(APIView):
 # 查看所有书籍
    def get(self, request):
        book_list = Book.objects.all()
        bs = BookSerializers(book_list, many=True)
        return Response(bs.data)

 # 添加书籍
    def post(self, request):
        bs = BookSerializers(data=request.data)
        if bs.is_valid():  # 验证数据是否有误
            print(bs.validated_data)  # 验证过后正确的数据: OrderedDict([('title', '三国演义'), ('price', 100), ('pub_date', datetime.date(2012, 12, 12)), ('publish', <Publish: 东莞出版社>), ('authors', [<Author: Kevin>, <Author: Aimer>])])
            bs.save()  # 保存数据,实际上调用的是 rest-framework 中的 create 方法
            return Response(bs.data)
        else:
            return Response(bs.errors)  # bs.errors 验证过后的错误信息


class BookDetailView(APIView):
 # 查看指定的书籍
    def get(self, request, id):
        book_obj = Book.objects.filter(pk=id).first()
        if book_obj:
            bs = BookSerializers(book_obj)
            return Response(bs.data)
        else:
            return Response({'static': 0, 'message': '没有该书籍'})

# 修改书籍
    def put(self, request, id):
        book_obj = Book.objects.filter(pk=id).first()
        bs = BookSerializers(book_obj, data=request.data)  # 修改数据的时候一定要指定修改那条数据(即: book_obj),否则在执行 .save() 的时候就相当于新增数据
        if bs.is_valid():  # 验证数据是否有误
            bs.save()  # 保存数据,实际上调用的是 rest-framework 中的 update 方法
            return Response(bs.data)
        else:
            return Response(bs.errors)  # bs.errors 验证过后的错误信息

# 删除书籍
    def delete(self, request, id):
        Book.objects.filter(pk=id).delete()
        return Response()

# urls.py

urlpatterns = [
……
    url(r'book/$', BookView.as_view()),
    url(r'book/(\d+)', BookDetailView.as_view())
]

# 接口说明: 查看所有书籍

# 接口: http://127.0.0.1:8000/book/

# 请求类型: GET

# 结果:

[
    {
        "id": 2,
        "title": "三体",
        "price": 222,
        "pub_date": null,
        "publish": 1,
        "authors": [
            2
        ]
    },
    {
        "id": 5,
        "title": "三国演义",
        "price": 100,
        "pub_date": "2012-12-12",
        "publish": 1,
        "authors": [
            1,
            2
        ]
    }
]

# --------------------------------------------------------------

# 接口说明: 添加书籍

# 接口: http://127.0.0.1:8000/book/

# 请求类型: POST

# 发送数据的编码格式: application/json -> 即: json 格式

# 所要发送的数据: {"title":"勿忘我","price":100,"pub_date":"2012-12-12","publish":1,"authors":[1,2]}

# 结果:

{
    "id": 14,
    "title": "勿忘我",
    "price": 100,
    "pub_date": "2012-12-12",
    "publish": 1,
    "authors": [
        1,
        2
    ]
}

# --------------------------------------------------------------

# 接口说明: 查看指定书籍

# 接口: http://127.0.0.1:8000/book/2

# 请求类型: GET

# 结果:

{
    "id": 2,
    "title": "三体",
    "price": 222,
    "pub_date": null,
    "publish": 1,
    "authors": [
        2
    ]
}

# --------------------------------------------------------------

# 接口说明: 修改书籍

# 接口: http://127.0.0.1:8000/book/2

# 请求类型: PUT

# 发送数据的编码格式: application/json -> 即: json 格式

# 所要发送的数据: {"title":"三体(第二部)","price":100,"pub_date":"2012-12-12","publish":1,"authors":[1,2]}

# 结果:

{
    "id": 2,
    "title": "三体(第二部)",
    "price": 100,
    "pub_date": "2012-12-12",
    "publish": 1,
    "authors": [
        2,
        1
    ]
}

# --------------------------------------------------------------

# 接口说明: 删除书籍

# 接口: http://127.0.0.1:8000/book/11

# 请求类型: DELETE

# 结果:


2.save() 方法的说明

  • 执行 save() 之前一定要执行 is_valid() 方法,否则会报错

  • .save() 方法的返回值就是所添加/修改的该条数据对象

# views.py

from django.shortcuts import render, HttpResponse
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import *
from .serializer import *


class BookView(APIView):
# 添加书籍
    def post(self, request):
        bs = BookSerializers(data=request.data)
        if bs.is_valid():  # 验证数据是否有误
data_obj = bs.save()
            print(data_obj.title, data_obj.price)  # 三体 100
            return Response(bs.data)
        else:
            return Response(bs.errors)

3.自定制字段

  • 当 fields = '__all__' 的时候,但是又想对里面的某些字段进行单独配置的时候,可以直接在 ModelSerializer 类中重新编写该字段,那么该字段的配置就会覆盖掉之前的配置

  • 建议: 如果使用了 ModelSerializer 就尽量不要自定制字段,否则容易出现问题

  • 注意: 
    • 当自定制一对多字段的时候,并且使用了source参数,那么就需要重写save()方法中的create()和update()方法 
    • 当自定制多对多字段的时候,添加或修改数据多对多字段的数据会有问题

# serializer.py 

class BookSerializers(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = '__all__'
        extra_kwargs = {
            "title": {
                'error_messages': {
                    'required': '书籍名称不能为空'
                }
            },
            'price': {
                'error_messages': {
                    'required': '价格不能为空'
                }
            },
            'pub_date': {
                'error_messages': {
                    'required': '日期不能为空'
                }
            },
            'publish': {
                'error_messages': {
                    'required': '出版社不能为空'
                }
            },
            'author': {
                'error_messages': {
                    'required': '作者不能为空'
                }
            }
        }

    publish = serializers.CharField(source="publish.name")  # 重新编写 publish 字段的配置

4. 重写 save() 中的 create() 和 update() 方法

  • .save()方法实际上调用的是 rest-framework 中的 create 或者 update 方法

  • 重写create()/update()方法就是自己将提交过来的数据保存到数据库,并且返回当前保存的数据

  • 什么时候需要重写

    • 当使用了自定制字段,并且在字段方法中传递了source参数 -> 通俗理解: 当自定制一对多字段的时候,并且使用了source参数,那么就需要重写save()方法中的create()和update()方法 

# serializer.py

class BookSerializers(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = '__all__'
        extra_kwargs = {
            "title": {
                'error_messages': {
                    'required': '书籍名称不能为空'
                }
            },
            'price': {
                'error_messages': {
                    'required': '价格不能为空'
                }
            },
            'pub_date': {
                'error_messages': {
                    'required': '日期不能为空'
                }
            },
            'publish': {
                'error_messages': {
                    'required': '出版社不能为空'
                }
            },
            'author': {
                'error_messages': {
                    'required': '作者不能为空'
                }
            }
        }

    publish = serializers.CharField(source="publish.pk")  # 重新编写 publish 字段的配置

重写 save() 中的 create() 方法
    def create(self, validated_data):
"""
        :param validated_data: 通过验证的数据
        """
        print(validated_data)

        book = Book.objects.create(title=validated_data['title'], price=validated_data['price'], pub_date=validated_data['pub_date'], publish_id=validated_data['publish']['pk'])
        book.authors.add(*validated_data["authors"])

        return book

重写 save() 中的 update() 方法
    def update(self, instance, validated_data):
"""
        :param instance: 需要修改的查询到的数据对象
        :param validated_data: 通过验证的数据
        """
        for key, val in validated_data.items():
            if hasattr(instance, key):
                if 'publish' == key:
                    instance.publish_id = validated_data['publish']['pk']
                elif 'authors' == key:
                    instance.authors.set(validated_data['authors'])
                else:
                    setattr(instance, key, val)
        instance.save()
        return instance

超链接API: Hyperlinked


1.HyperlinkedIdentityField 字段方法

  • HyperlinkedIdentityField 字段方法一般都是用在一对多字段上
  • HyperlinkedIdentityField 字段方法返回的是一个url路径
  • HyperlinkedIdentityField 字段方法一般在前后端分离的时候很少会使用
  • 使用场景: 分页器
  • 注意: 如果你所定义的 序列化类 使用了 HyperlinkedIdentityField 字段方法,那么在实例化 序列化类 的时候一定要设置 context={'request': request},否则会报错

# urls.py

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'publish/(?P<pk>\d+)', PublishDetailView.as_view(), name='publish_detail'),
    url(r'book/$', BookView.as_view())
]

# serializer.py

from rest_framework import serializers
from .models import *


class PublishSerializers(serializers.Serializer):
    name = serializers.CharField()
    email = serializers.CharField()


class BookSerializers(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = '__all__'

# view_name -> url别名
    # lookup_field -> 当前外键字段(一对多)在数据库中的字段名
    # lookup_url_kwarg -> 分组命名匹配中的别名

    publish = serializers.HyperlinkedIdentityField(view_name='publish_detail', lookup_field='publish_id', lookup_url_kwarg='pk')

# views.py

from django.shortcuts import render, HttpResponse
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import *
from .serializer import *


class BookView(APIView):
# 查看所有书籍
    def get(self, request):
        book_list = Book.objects.all()
        bs = BookSerializers(book_list, many=True, context={'request': request})  # 如果你所定义的 序列化类 使用了 HyperlinkedIdentityField 字段方法,那么在实例化 序列化类 的时候一定要设置 context={'request': request},否则会报错
        return Response(bs.data)


class PublishDetailView(APIView):
# 查看指定的出版社
    def get(self, request, pk):
        publish_obj = Publish.objects.filter(pk=pk).first()
        if publish_obj:
            ps = PublishSerializers(publish_obj)
            return Response(ps.data)
        else:
            return Response({'static': 0, 'message': '没有该出版社'})

# 接口: http://127.0.0.1:8000/book/

# 请求类型: GET

# 结果:

[
    {
        "id": 2,
"publish": "http://127.0.0.1:8000/publish/1",
        "title": "三体",
        "price": 222,
        "pub_date": null,
        "authors": [
            2
        ]
    },

……

]